﻿#define _CRT_SECURE_NO_WARNINGS 1
#include "snake.h"
void SetPos(short x, short y)
{
	//获得标准输出设备的句柄
	HANDLE houtput = NULL;
	houtput = GetStdHandle(STD_OUTPUT_HANDLE);

	//定位光标的位置
	COORD pos = { x,y };

	//第一个参数句柄,第二个参数位置
	SetConsoleCursorPosition(houtput, pos);
}
void CreateMap()
{
	//上
	for (int i = 0; i < 29; i++)
	{
		wprintf(L"%lc", WALL);
	}
	//下
	SetPos(0, 26);
	for (int i = 0; i < 29; i++)
	{
		wprintf(L"%lc", WALL);
	}
	//左
	for (int i = 1; i <= 25; i++)
	{
		SetPos(0, i);
		wprintf(L"%lc", WALL);
	}
	//右
	for (int i = 1; i <= 25; i++)
	{
		SetPos(56, i);
		wprintf(L"%lc", WALL);
	}
}
void WelcomeToGame()
{
	//定位光标位置,打印提示信息
	SetPos(40, 14);
	wprintf(L"欢迎来到贪吃蛇小游戏\n");
	SetPos(42, 20);
	system("pause");//暂停
	system("cls");//清理屏幕
	SetPos(25, 14);
	wprintf(L"用↑.↓.←.→分别控制蛇的移动，F3为加速，F4为减速\n");
	SetPos(25, 15);
	wprintf(L"加速能够得到更高的分数");

	SetPos(42, 20);
	system("pause");
	system("cls");
	
}
void InitSnake(pSnake ps)
{
	pSnakeNode cur = NULL;
	for (int i = 0; i < 5; i++)
	{
		cur = (pSnakeNode)malloc(sizeof(SnakeNode));
		if (cur == NULL)
		{
			perror("InitSnake::malloc error!\n");
			return;
		}
		cur->next = NULL;
		cur->x = POS_X + i * 2;
		cur->y = POS_Y;

		//头插
		if (ps->_pSnake == NULL)//蛇头指针为空
		{
			ps->_pSnake = cur;
		}
		else//非空
		{
			cur->next = ps->_pSnake;
			ps->_pSnake = cur;
		}
	}
	//打印蛇的身体
	cur = ps->_pSnake;
	while (cur)
	{
		SetPos(cur->x, cur->y);
		wprintf(L"%lc", BODY);
		cur = cur->next;
	}

	//设置贪吃蛇的属性
	ps->_dir = RIGHT;//默认向右
	ps->_score = 0;//总成绩
	ps->_good_weight = 10;//一个食物10分
	ps->_sleep_time = 200;//单位是毫秒
	ps->_status = OK;//状态是OK
}
void CreateFood(pSnake ps)
{
	int x = 0;
	int y = 0;
	//随机生成食物
	begin:
	do
	{
		x = rand() % 53 + 2;
		y = rand() % 25 + 1;
	} while (x % 2 != 0);

	//判断食物是否和蛇身重合
	pSnakeNode cur = ps->_pSnake;
	while (cur)
	{
		if (cur->x == x && cur->y == y)
		{
			goto begin;
		}
		cur = cur->next;
	}
	//创建食物
	pSnakeNode food = (pSnakeNode)malloc(sizeof(SnakeNode));
	if (food == NULL)
	{
		perror("CreateFood::malloc error!\n");
		return;
	}
	food->x = x;
	food->y = y;
	food->next = NULL;
	//打印食物
	SetPos(x, y);
	wprintf(L"%lc", FOOD);
	ps->_pFood = food;
}
void GameStart(pSnake ps)
{
	//1.设置窗口的大小和窗口名字和光标隐藏
	system("mode con cols=100 lines=30");
	system("title 贪吃蛇");
	HANDLE houtput = GetStdHandle(STD_OUTPUT_HANDLE);
	//隐藏光标
	CONSOLE_CURSOR_INFO cursor_info = { 0 };
	GetConsoleCursorInfo(houtput, &cursor_info);
	cursor_info.bVisible = false;
	SetConsoleCursorInfo(houtput, &cursor_info);
	//2.欢迎界面和功能介绍
	WelcomeToGame();
	//3.绘制地图
	CreateMap();
	//4.创建蛇
	InitSnake(ps);
	//5.创建食物
	CreateFood(ps);
}
void PrintHelpInfo()
{
	SetPos(64, 10);
	wprintf(L"%ls", L"不能穿墙,不能咬到自己\n");
	SetPos(64, 11);
	wprintf(L"%ls", L"用↑.↓.←.→分别控制蛇的移动\n");
	SetPos(64, 12);
	wprintf(L"%ls", L"按F3加速,F4减速\n");
	SetPos(64, 13);
	wprintf(L"%ls", L"按esc退出游戏,按空格暂停游戏\n");
	SetPos(64, 15);
	wprintf(L"%ls", L"2024-9-4\n");
}
//游戏暂停函数
void pause()
{
	while (1)
	{
		Sleep(2000);
		if (KEY_PRESS(VK_SPACE))
		{
			break;
		}
	}
}
//pSnakeNode pn - 下一个节点的地址
//pSnake ps - 蛇的指针
bool NextIsFood(pSnakeNode pn, pSnake ps)
{
	return (pn->x == ps->_pFood->x) && (pn->y == ps->_pFood->y);
}

void EatFood(pSnakeNode pn, pSnake ps)
{
	//将下一个节点头插
	pn->next = ps->_pSnake;
	ps->_pSnake = pn;
	//打印蛇
	pSnakeNode cur = ps->_pSnake;
	while(cur)
	{
		SetPos(cur->x, cur->y);
		wprintf(L"%lc", BODY);
		cur = cur->next;
	}
	//加分
	ps->_score +=  ps->_good_weight;
	//释放食物节点
	free(ps->_pFood);
	CreateFood(ps);
}

void NoFood(pSnakeNode pn, pSnake ps)
{
	//头插
	pn->next = ps->_pSnake;
	ps->_pSnake = pn;
	//打印蛇
	pSnakeNode cur = ps->_pSnake;
	while (cur->next->next)
	{
		SetPos(cur->x, cur->y);
		wprintf(L"%lc", BODY);
		cur = cur->next;
	}
	//将尾节点打印空白字符
	SetPos(cur->next->x, cur->next->y);
	printf("  ");
	//释放最后一个节点
	free(cur->next);
	cur->next = NULL;

}
void KillByWall(pSnake ps)
{
	if (ps->_pSnake->x == 0 || ps->_pSnake->x == 56
		|| ps->_pSnake->y == 0 || ps->_pSnake->y == 26)
	{
		ps->_status = KILL_BY_WALL;
	}
}
void KillBySelf(pSnake ps)
{
	pSnakeNode cur = ps->_pSnake->next;
	while (cur)
	{
		if (cur->x == ps->_pSnake->x && cur->y == ps->_pSnake->y)
		{
			ps->_status = KILL_BY_SELF;
			break;
		}
		cur = cur->next;
	}
}
void SnakeMove(pSnake ps)
{
	//创建节点
	pSnakeNode pNextNode = (pSnakeNode)malloc(sizeof(SnakeNode));
	if (pNextNode == NULL)
	{
		perror("SnakeMove()::malloc() error!\n");
		return;
	}
	switch (ps->_dir)
	{
	case UP:
		pNextNode->x = ps->_pSnake->x;
		pNextNode->y = ps->_pSnake->y - 1;
		break;
	case DOWN:
		pNextNode->x = ps->_pSnake->x;
		pNextNode->y = ps->_pSnake->y + 1;
		break;
	case LEFT:
		pNextNode->x = ps->_pSnake->x - 2;
		pNextNode->y = ps->_pSnake->y;
		break;
	case RIGHT:
		pNextNode->x = ps->_pSnake->x + 2;
		pNextNode->y = ps->_pSnake->y;
		break;
	}
	//判断是否是食物
	if (NextIsFood(pNextNode,ps))
	{
		//吃食物
		EatFood(pNextNode, ps);
	}
	else
	{
		//做下一步不是食物的处理
		NoFood(pNextNode, ps);
	}
	//检测蛇是否撞墙
	KillByWall(ps);
	//检测蛇是否撞到自己
	KillBySelf(ps);
}


void GameRun(pSnake ps)
{
	//打印帮助信息
	PrintHelpInfo();
	//按键检测
	do
	{
		//打印总分数和食物分数
		SetPos(64, 7);
		wprintf(L"总分数: %d\n",ps->_score);
		SetPos(75, 7);
		wprintf(L"每个食物得分: %2d\n", ps->_good_weight);
		if (KEY_PRESS(VK_UP) && ps->_dir != DOWN)
		{
			//上
			ps->_dir = UP;
		}
		else if (KEY_PRESS(VK_DOWN) && ps->_dir != UP)
		{
			//下
			ps->_dir = DOWN;
		}
		else if (KEY_PRESS(VK_LEFT) && ps->_dir != RIGHT)
		{
			//左
			ps->_dir = LEFT;
		}
		else if (KEY_PRESS(VK_RIGHT) && ps->_dir != LEFT)
		{
			//右
			ps->_dir = RIGHT;
		}
		else if (KEY_PRESS(VK_SPACE))
		{
			//暂停
			pause();
		}
		else if (KEY_PRESS(VK_ESCAPE))
		{
			//ESC退出
			ps->_status = END_NORMAL;
			break;
		}
		else if (KEY_PRESS(VK_F3))
		{
			//F3加速
			if (ps->_sleep_time>120)
			{
				ps->_sleep_time -= 20;
				ps->_good_weight += 2;
			}
		}
		else if (KEY_PRESS(VK_F4))
		{
			//F4减速
			if (ps->_good_weight > 2)
			{
				ps->_sleep_time += 20;
				ps->_good_weight -= 2;
			}
		}
		//蛇的休眠时间
		Sleep(ps->_sleep_time);
		//蛇的移动
		SnakeMove(ps);
		
	} while (ps->_status == OK);
}
void GameEnd(pSnake ps)
{
	//定位光标
	SetPos(24, 13);
	//判断状态
	switch (ps->_status)
	{
	case KILL_BY_WALL:
		wprintf(L"您撞到了墙上");
		break;
	case KILL_BY_SELF:
		wprintf(L"您撞到了自己");
		break;
	case END_NORMAL:
		wprintf(L"您主动退出了游戏");
		break;
	}
	//释放节点
	pSnakeNode cur = ps->_pSnake;
	while (cur)
	{
		pSnakeNode del = cur;
		cur = cur->next;
		free(del);
		del = NULL;
		
	}
}

